1   /*
2    * Copyright (C) 2014 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.util.concurrent;
18  
19  import static com.google.common.base.Preconditions.checkNotNull;
20  
21  import com.google.caliper.Benchmark;
22  import com.google.caliper.Param;
23  import com.google.caliper.api.VmOptions;
24  import com.google.common.collect.ImmutableList;
25  
26  import java.util.concurrent.Callable;
27  import java.util.concurrent.CancellationException;
28  import java.util.concurrent.ExecutionException;
29  import java.util.concurrent.Executor;
30  
31  /**
32   * A benchmark for {@link Futures#combine}
33   */
34  @VmOptions({"-Xms12g", "-Xmx12g", "-d64"})
35  public class FuturesCombineBenchmark {
36  
37    enum Impl {
38      OLD {
39        @Override <V> ListenableFuture<V> combine(final Callable<V> combiner, Executor executor,
40            Iterable<? extends ListenableFuture<?>> futures) {
41          ListenableFuture<?> trigger = Futures.successfulAsList(futures);
42          checkNotNull(combiner);
43          checkNotNull(trigger);
44          return Futures.transform(trigger, new AsyncFunction<Object, V>() {
45            @Override public ListenableFuture<V> apply(Object arg) throws Exception {
46              try {
47                return Futures.immediateFuture(combiner.call());
48              } catch (CancellationException e) {
49                return Futures.immediateCancelledFuture();
50              } catch (ExecutionException e) {
51                return Futures.immediateFailedFuture(e.getCause()); // OK to rethrow on Error
52              }
53            }
54          }, executor);
55        }
56      },
57      NEW {
58        @Override
59        <V> ListenableFuture<V> combine(Callable<V> combiner, final Executor executor,
60            Iterable<? extends ListenableFuture<?>> futures) {
61          return Futures.combine(combiner, executor, futures);
62        }
63      };
64  
65      abstract <V> ListenableFuture<V> combine(
66          Callable<V> combiner, Executor executor,
67          Iterable<? extends ListenableFuture<?>> futures);
68    }
69  
70    private static final Executor INLINE_EXECUTOR = new Executor() {
71      @Override public void execute(Runnable command) {
72        command.run();
73      }
74    };
75  
76    @Param Impl impl;
77    @Param({"1", "5", "10"}) int numInputs;
78  
79    @Benchmark int timeDoneSuccesfulFutures(int reps) throws Exception {
80      ImmutableList.Builder<ListenableFuture<?>> futuresBuilder = ImmutableList.builder();
81      for (int i = 0; i < numInputs; i++) {
82        futuresBuilder.add(Futures.immediateFuture(i));
83      }
84      ImmutableList<ListenableFuture<?>> futures = futuresBuilder.build();
85      Impl impl = this.impl;
86      Callable<Integer> callable = Callables.returning(12);
87      int sum = 0;
88      for (int i = 0; i < reps; i++) {
89        sum += impl.combine(callable, INLINE_EXECUTOR, futures).get();
90      }
91      return sum;
92    }
93    
94    @Benchmark int timeDoneFailedFutures(int reps) throws Exception {
95      ImmutableList.Builder<ListenableFuture<?>> futuresBuilder = ImmutableList.builder();
96      for (int i = 0; i < numInputs; i++) {
97        futuresBuilder.add(Futures.immediateFailedFuture(new Exception("boom")));
98      }
99      ImmutableList<ListenableFuture<?>> futures = futuresBuilder.build();
100     Impl impl = this.impl;
101     Callable<Integer> callable = Callables.returning(12);
102     int sum = 0;
103     for (int i = 0; i < reps; i++) {
104       sum += impl.combine(callable, INLINE_EXECUTOR, futures).get();
105     }
106     return sum;
107   }
108   
109   @Benchmark int timeSuccesfulFutures(int reps) throws Exception {
110     Impl impl = this.impl;
111     Callable<Integer> callable = Callables.returning(12);
112     int sum = 0;
113     for (int i = 0; i < reps; i++) {
114       ImmutableList<SettableFuture<Integer>> futures = getSettableFutureList();
115       ListenableFuture<Integer> combined = impl.combine(callable, INLINE_EXECUTOR, futures);
116       for (SettableFuture<Integer> future : futures) {
117         future.set(i);
118       }
119       sum += combined.get();
120     }
121     return sum;
122   }
123 
124   @Benchmark int timeFailedFutures(int reps) throws Exception {
125     Impl impl = this.impl;
126     Callable<Integer> callable = Callables.returning(12);
127     int sum = 0;
128     Exception throwable = new Exception("boom");
129     for (int i = 0; i < reps; i++) {
130       ImmutableList<SettableFuture<Integer>> futures = getSettableFutureList();
131       ListenableFuture<Integer> combined = impl.combine(callable, INLINE_EXECUTOR, futures);
132       for (SettableFuture<Integer> future : futures) {
133         future.setException(throwable);
134       }
135       sum += combined.get();
136     }
137     return sum;
138   }
139 
140   private ImmutableList<SettableFuture<Integer>> getSettableFutureList() {
141     ImmutableList.Builder<SettableFuture<Integer>> futuresBuilder = ImmutableList.builder();
142     for (int i = 0; i < numInputs; i++) {
143       futuresBuilder.add(SettableFuture.<Integer>create());
144     }
145     return futuresBuilder.build();
146   }
147 }